/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use openssl::hash::{hash, MessageDigest};

use common_log::error;
use crate::entities::attest_request::Measurement;
use crate::entities::token::{PolicyInfo, PolicyMatchedInfo};
use crate::entities::token::token_trait::{AttesterResult, Token, TokenType};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct EatToken {
    pub nonce_type: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub intuse: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eat_nonce: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attester_data: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ueid: Option<String>,
    pub matched_policy: Vec<PolicyMatchedInfo>,
    pub unmatched_policy: Vec<PolicyMatchedInfo>,
    #[serde(flatten)]
    pub results: HashMap<String, EatAttesterResult>,
}

impl EatToken {
    pub fn new() -> Self {
        Self {
            nonce_type: String::new(),
            intuse: None,
            eat_nonce: None,
            attester_data: None,
            ueid: None,
            matched_policy: Vec::new(),
            unmatched_policy: Vec::new(),
            results: HashMap::new(),
        }
    }

    pub fn get_ueid(attester_data: &HashMap<String, EatAttesterResult>) -> Option<String> {
        for (_, eat_attester_result) in attester_data {
            if let Some(raw_evidence) = &(eat_attester_result.raw_evidence) {
                if let Some(ueid) = raw_evidence.get("ueid") {
                    return Some(ueid.to_string());
                }
            }
        }
        None
    }

    fn calculate_sha256(content: &str) -> String {
        match hash(MessageDigest::sha256(), content.as_bytes()) {
            Ok(digest) => {
                digest.iter().map(|b| format!("{:02x}", b)).collect::<String>()
            },
            Err(_) => String::new(),
        }
    }
}


#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct EatAttesterResult {
    pub attestation_status: String,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub policy_info: Vec<PolicyInfo>,
    #[serde(flatten)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub raw_evidence: Option<serde_json::Value>,
}

impl Token for EatToken {
    fn create_evidence_response(
        &mut self,
        verify_results: Vec<bool>,
        raw_evidence: Option<serde_json::Value>,
        policy_info: Vec<PolicyInfo>,
    ) -> AttesterResult {
        let attestation_status = if policy_info.is_empty() {
            "unknown"
        } else if verify_results.iter().all(|&x| x) {
            "pass"
        } else {
            "fail"
        };

        for policy in &policy_info {
            let policy_matched_info = PolicyMatchedInfo {
                id: policy.appraisal_policy_id.clone(),
                version: policy.policy_version,
                sha256: Self::calculate_sha256(&policy.policy_content),
            };

            if policy.policy_matched {
                self.matched_policy.push(policy_matched_info);
            } else {
                self.unmatched_policy.push(policy_matched_info);
            }
        }

        let processed_evidence = match &raw_evidence {
            Some(evidence) => {
                evidence.get("annotated_evidence").cloned()
            },
            None => None,
        };

        AttesterResult::Eat(EatAttesterResult {
            attestation_status: attestation_status.to_string(),
            raw_evidence: processed_evidence,
            policy_info,
        })
    }

    fn create_attestation_response(
        &self,
        evidence_token_responses: &HashMap<String, AttesterResult>,
        nonce_type: &str,
        nonce: &Option<String>,
        measurement: &Measurement,
    ) -> TokenType {
        let mut eat_evidence_responses = HashMap::new();
        for (key, value) in evidence_token_responses {
            if let AttesterResult::Eat(eat_result) = value {
                eat_evidence_responses.insert(key.clone(), eat_result.clone());
            }
        }
        let ueid = match Self::get_ueid(&eat_evidence_responses) {
            Some(ueid) => ueid,
            None => measurement.node_id.clone()
        };
        TokenType::Eat(EatToken {
            eat_nonce: match nonce_type {
                "verifier" | "user" => match serde_json::to_value(nonce) {
                    Ok(value) => Some(value),
                    Err(e) => {
                        error!("Failed to serialize user nonce: {}", e);
                        None
                    },
                },
                _ => None,
            },
            nonce_type: nonce_type.to_string(),
            attester_data: measurement.attester_data.clone(),
            results: eat_evidence_responses,
            intuse: Some("Generic".to_string()),
            ueid: Some(ueid),
            matched_policy: self.matched_policy.clone(),
            unmatched_policy: self.unmatched_policy.clone(),
        })
    }
}